package com.google.code.swingonfire.plotter;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.swing.Icon;
import javax.swing.ImageIcon;

public class Plotter
{

    private static final BufferedImage DEFAULT_IMAGE = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);

    private final Sketch sketch;

    private Color color = null;
    private Dimension size = null;
    private Font sizeByFont = null;
    private Double rotate = null;

    public Plotter(Sketch sketch)
    {
        super();

        this.sketch = sketch;
    }

    public Sketch getSketch()
    {
        return sketch;
    }

    public Plotter color(Color color)
    {
        this.color = color;

        return this;
    }

    public Plotter size(int size)
    {
        return size(size, size);
    }

    public Plotter size(int width, int height)
    {
        return size(new Dimension(width, height));
    }

    public Plotter size(Dimension size)
    {
        this.size = size;

        return this;
    }

    public Plotter size(Font sizeByFont)
    {
        this.sizeByFont = sizeByFont;

        return this;
    }

    public Plotter forComponent(Component component)
    {
        size(component.getFont());
        color(component.getForeground());

        return this;
    }

    public Plotter rotate(Double rotate)
    {
        this.rotate = rotate;

        return this;
    }

    public void draw(Graphics g, int x, int y)
    {
        draw(g, x, y);
    }

    public void draw(Graphics2D g, int x, int y)
    {
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.translate(x, y);
        drawSketch(g2d);
    }

    public void draw(Graphics g)
    {
        draw((Graphics2D) g);
    }

    public void draw(Graphics2D g)
    {
        drawSketch((Graphics2D) g.create());
    }

    public void drawBounds(Graphics g, int x, int y)
    {
        drawBounds(g, x, y);
    }

    public void drawBounds(Graphics2D g, int x, int y)
    {
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.translate(x, y);
        drawSketchBounds(g2d);
    }

    public void drawBounds(Graphics g)
    {
        drawBounds((Graphics2D) g);
    }

    public void drawBounds(Graphics2D g)
    {
        drawSketchBounds((Graphics2D) g.create());
    }

    protected Rectangle getScaledBounds(Graphics2D g, Rectangle bounds)
    {
        double scale = getScale(g, bounds);

        return new Rectangle((int) Math.floor(bounds.x * scale), (int) Math.floor(bounds.y * scale),
            (int) Math.ceil(bounds.width * scale), (int) Math.ceil(bounds.height * scale));
    }

    protected double getScale(Graphics2D g, Rectangle bounds)
    {
        double scale = Double.MAX_VALUE;

        if (size != null)
        {
            scale =
                Math.min(Math.min(size.getWidth() / bounds.getWidth(), size.getHeight() / bounds.getHeight()), scale);
        }

        if (sizeByFont != null)
        {
            if (g == null)
            {
                g = DEFAULT_IMAGE.createGraphics();
            }

            FontMetrics fontMetrics = g.getFontMetrics(sizeByFont);
            //int height = fontMetrics.getAscent() + fontMetrics.getDescent();
            int height = fontMetrics.getHeight();
            scale = Math.min(Math.min(height / bounds.getWidth(), height / bounds.getHeight()), scale);
        }

        if (scale == Double.MAX_VALUE)
        {
            scale = 1;
        }
        return scale;
    }

    protected void drawSketch(Graphics2D g)
    {
        prepareGraphics(g);

        sketch.draw(g);

    }

    protected void drawSketchBounds(Graphics2D g)
    {
        prepareGraphics(g);

        g.fillRect(sketch.getBounds().x, sketch.getBounds().y, sketch.getBounds().width, sketch.getBounds().height);
    }

    private void prepareGraphics(Graphics2D g)
    {
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

        double scale = getScale(g, sketch.getBounds());

        if (scale != 1)
        {
            g.scale(scale, scale);
        }

        if (rotate != null)
        {
            g.rotate(rotate.doubleValue());
        }

        if (color != null)
        {
            g.setColor(color);
        }
    }

    public Image toImage()
    {
        Rectangle bounds = getScaledBounds(null, sketch.getBounds());
        BufferedImage image = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();

        g.setColor(Color.ORANGE);
        g.drawRect(0, 0, bounds.width - 1, bounds.height - 1);
        System.out.println(bounds);
        draw(g, -bounds.x, -bounds.y);

        return image;
    }

    public Icon toIcon()
    {
        return new ImageIcon(toImage());
    }
}
